/**
 * Baijiahulian.com Inc. Copyright (c) 2014-2016 All Rights Reserved.
 */
package com.baijia.util;

import com.baijia.passport.core.RedisTemplate;
import com.baijia.passport.core.RedisTemplate.Callback;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.math.RandomUtils;

import redis.clients.jedis.Jedis;

import java.util.HashMap;
import java.util.Map;

import lombok.extern.slf4j.Slf4j;

/**
 * @title MobileLoginUtil
 * @desc 使用验证码登录的工具类
 * @author macrohuang
 * @date Mar 30, 2016
 * @version 1.0
 */

@Slf4j
public class MobileLoginUtil {

    private static final String MOBILE_BLOCK = "passport_m_block";
    private static final String MOBILE_BLOCK_HACK = "passport_m_block_hack";
    private static final String MOBILE_LOGIN_RETRY = "passport_m_retry";
    private static final String MOBILE_LOGIN_RETRY_HACK = "passport_m_retry_hack";
    private static final String SEND_SEPERATE = "passport_m_send_sepreate";
    private static final String VALID_SEPERATE = "passport_m_valid_sepreate";
    private static final String VALID_SEPERATE_HACK = "passport_m_valid_sepreate_hack";
    private static final String MOBILE_PASSCODE = "passport_m_passcode";
    private static final String PASSCODE_TEMPLATE = "您收到的验证码为：%d，请勿泄露给第三方！";
    public static final String LOGIN_PASSWORD_TEMPLATE = "您注册了跟谁学账号，赶快登录激活吧，用户名: %s, 密码：%s, 为了您的帐号安全, 请尽快修改密码。";

    private static final int PASSCODE_EXPIRY_IN_SECONDS = 60 * 60;// 生成的验证码有效期
    private static final int PASSCODE_INTERVAL_IN_SECONDS = 60;// 生成的验证码或验证验证码的间隔

    private static final String PLACEHOLDER = "1";

    private final static String COUNTRY_CODE_CHINA = "CN";

    public static boolean isUserBlock(final String mobile) {
        return RedisTemplate.execute(new Callback<Boolean>() {

            @Override
            public Boolean doInInstance(Jedis jedis) {
                return StringUtils.isNotBlank(jedis.get(MOBILE_BLOCK + mobile));
            }
        });
    }

    public static boolean isUserBlockHack(final String mobile) {
        return RedisTemplate.execute(new Callback<Boolean>() {

            @Override
            public Boolean doInInstance(Jedis jedis) {
                return StringUtils.isNotBlank(jedis.get(MOBILE_BLOCK_HACK + mobile));
            }
        });
    }

    public static void blockUserHack(final String mobile, final int blockInSeconds) {
        RedisTemplate.execute(new Callback<Void>() {

            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.setex(MOBILE_BLOCK_HACK + mobile, blockInSeconds, PLACEHOLDER);
                return null;
            }
        });
        delUserRetriesHack(mobile);
    }

    public static void blockUser(final String mobile, final int blockInSeconds) {
        RedisTemplate.execute(new Callback<Void>() {

            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.setex(MOBILE_BLOCK + mobile, blockInSeconds, PLACEHOLDER);
                return null;
            }
        });
        delUserRetries(mobile);
    }

    public static void unblockUser(final String mobile) {
        RedisTemplate.execute(new Callback<Void>() {

            /*
             * (non-Javadoc)
             * 
             * @see com.baijia.passport.core.RedisTemplate.Callback#doInInstance(redis.clients.jedis.Jedis)
             */
            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.del(MOBILE_BLOCK + mobile);
                return null;
            }
        });
    }

    public static boolean canUserLoginHack(final String mobile) {
        return RedisTemplate.execute(new Callback<Boolean>() {

            /*
             * (non-Javadoc)
             * 
             * @see com.baijia.passport.core.RedisTemplate.Callback#doInInstance(redis.clients.jedis.Jedis)
             */
            @Override
            public Boolean doInInstance(Jedis jedis) {
                return StringUtils.isBlank(jedis.get(VALID_SEPERATE_HACK + mobile));
            }
        });
    }

    public static boolean canUserLogin(final String mobile) {
        return RedisTemplate.execute(new Callback<Boolean>() {
            @Override
            public Boolean doInInstance(Jedis jedis) {
                return StringUtils.isBlank(jedis.get(VALID_SEPERATE + mobile));
            }
        });
    }

    public static boolean canSendPasscode(final String mobile) {
        return RedisTemplate.execute(new Callback<Boolean>() {

            /*
             * (non-Javadoc)
             * 
             * @see com.baijia.passport.core.RedisTemplate.Callback#doInInstance(redis.clients.jedis.Jedis)
             */
            @Override
            public Boolean doInInstance(Jedis jedis) {
                return StringUtils.isBlank(jedis.get(SEND_SEPERATE + mobile));
            }
        });
    }

    public static int getUserRetriesHack(final String mobile) {
        return RedisTemplate.execute(new Callback<Integer>() {

            /*
             * (non-Javadoc)
             * 
             * @see com.baijia.passport.core.RedisTemplate.Callback#doInInstance(redis.clients.jedis.Jedis)
             */
            @Override
            public Integer doInInstance(Jedis jedis) {
                String retry = jedis.get(MOBILE_LOGIN_RETRY_HACK + mobile);
                if (NumberUtils.isDigits(retry)) {
                    return Integer.parseInt(jedis.get(MOBILE_LOGIN_RETRY_HACK + mobile));
                } else {
                    return 0;
                }
            }
        });
    }

    public static int getUserRetries(final String mobile) {
        return RedisTemplate.execute(new Callback<Integer>() {

            /*
             * (non-Javadoc)
             * 
             * @see com.baijia.passport.core.RedisTemplate.Callback#doInInstance(redis.clients.jedis.Jedis)
             */
            @Override
            public Integer doInInstance(Jedis jedis) {
                String retry = jedis.get(MOBILE_LOGIN_RETRY + mobile);
                if (NumberUtils.isDigits(retry)) {
                    return Integer.parseInt(jedis.get(MOBILE_LOGIN_RETRY + mobile));
                } else {
                    return 0;
                }
            }
        });
    }

    public static void delUserRetries(final String mobile) {
        RedisTemplate.execute(new Callback<Void>() {
            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.del(MOBILE_LOGIN_RETRY + mobile);
                return null;
            }
        });
    }

    public static void delUserRetriesHack(final String mobile) {
        RedisTemplate.execute(new Callback<Void>() {

            /*
             * (non-Javadoc)
             * 
             * @see com.baijia.passport.core.RedisTemplate.Callback#doInInstance(redis.clients.jedis.Jedis)
             */
            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.del(MOBILE_LOGIN_RETRY_HACK + mobile);
                return null;
            }
        });
    }

    public static int genCode() {
        return RandomUtils.nextInt(100000) + 100000 * (RandomUtils.nextInt(9) + 1);
    }

    public static Map<String, String> sendPasscode(final String mobile, Integer isVoice) {
        final int code = genCode();
        Map<String, String> sent;
        if (mobile.startsWith("00")) {// 国际号码 发送国际短信验证码
            sent = SMSUtils.sendByChannel(mobile, String.valueOf(code), SMSUtils.I18N_SMS_TYPE, null);
        } else { // 普通号码
            if (isVoice == null || isVoice == 0) {
                sent = SMSUtils.sendByCode(mobile, String.format(PASSCODE_TEMPLATE, code));
            } else {
                sent = SMSUtils.sendByChannel(mobile, String.valueOf(code), SMSUtils.VOICE_SMS_TYPE, null);
            }
        }

        if (sent != null) {
            RedisTemplate.execute(new Callback<Void>() {
                @Override
                public Void doInInstance(Jedis jedis) {
                    jedis.setex(MOBILE_PASSCODE + mobile, PASSCODE_EXPIRY_IN_SECONDS, "" + code);
                    return null;
                }
            });
            // 发送过验证码后，1分钟内不允许再次发送
            touchSendSeperate(mobile);
        }

        return sent;
    }

    public static Map<String, String> sendPasscodeTest(final String mobile, Integer isVoice) {
        final int code = genCode();
        Map<String, String> sent = new HashMap<>();
        sent.put("code", String.valueOf(code));
        RedisTemplate.execute(new Callback<Void>() {

            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.setex(MOBILE_PASSCODE + mobile, PASSCODE_EXPIRY_IN_SECONDS, "" + code);
                return null;
            }
        });
        return sent;
    }

    public static void touchSendSeperate(final String mobile) {
        RedisTemplate.execute(new Callback<Void>() {

            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.setex(SEND_SEPERATE + mobile, PASSCODE_INTERVAL_IN_SECONDS, PLACEHOLDER);
                return null;
            }
        });
    }

    public static void delPasscode(final String mobile) {
        RedisTemplate.execute(new Callback<Void>() {

            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.del(MOBILE_PASSCODE + mobile);
                return null;
            }
        });
    }

    public static String getI18nMobile(String countrySymbol, String countryCode, String mobile) {
        if (!COUNTRY_CODE_CHINA.equals(countrySymbol)) {
            countryCode = countryCode.replace("+", "00");
            mobile = countryCode + mobile;
        }

        return mobile;
    }

    public static boolean validatePasscodeI18n(String countrySymbol, String countryCode, String mobile, String passcode) {
        mobile = getI18nMobile(countrySymbol, countryCode, mobile);
        return validPasscode(mobile, passcode);
    }

    public static boolean validPasscode(final String mobile, final String passcode) {
        String code = RedisTemplate.execute(new Callback<String>() {
            @Override
            public String doInInstance(Jedis jedis) {
                return jedis.get(MOBILE_PASSCODE + mobile);
            }
        });

        log.debug("validPasscode : mobile={}, newCode={}, oriCode={}", SecretCoverUtil.coverMobileWithAsterisk(mobile),
            passcode, code);
        boolean valid = StringUtils.equals(code, passcode);
        if (valid) { // 登录成功后，1分钟内不允许再次登录
            // RedisUtil.hSet(VALID_SEPERATE, mobile, PLACEHOLDER, PASSCODE_INTERVAL_IN_SECONDS);
            RedisTemplate.execute(new Callback<Void>() {
                @Override
                public Void doInInstance(Jedis jedis) {
                    jedis.setex(VALID_SEPERATE + mobile, PASSCODE_INTERVAL_IN_SECONDS, PLACEHOLDER);
                    return null;
                }
            });
            delUserRetries(mobile);
        }

        return valid;
    }

    public static void incRetry(final String mobile) {
        RedisTemplate.execute(new Callback<Void>() {

            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.incr(MOBILE_LOGIN_RETRY + mobile);
                return null;
            }
        });
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10000000; i++) {
            int code = genCode();
            if (code < 100000 || code >= 1000000) {
                System.out.println("Error code: " + code);
            }
            assert code >= 100000 && code < 1000000;
        }
    }

    public static void incRetryHack(final String mobile) {
        RedisTemplate.execute(new Callback<Void>() {

            @Override
            public Void doInInstance(Jedis jedis) {
                jedis.incr(MOBILE_LOGIN_RETRY_HACK + mobile);
                return null;
            }
        });
    }

}